#include "../pch.h"

namespace BFX
{

// Create a new empty string builder with the specified capacity.
REFUniStringBuilder::REFUniStringBuilder(int nCapacity)
{
	BFX_REQUIRE (nCapacity >= 0);
	if (nCapacity > 0)
	{
		m_strValue.GetBuffer(nCapacity);
	}
}

// Creates a new string builder from the specified string.
REFUniStringBuilder::REFUniStringBuilder(const UniString& str)
	: m_strValue(str)
{
}

// Creates a new string builder from the specifed substring.
REFUniStringBuilder::REFUniStringBuilder(UCSTR psz, int nOffset, int nLength)
	: m_strValue(psz + nOffset, nLength)
{
}

REFUniStringBuilder::~REFUniStringBuilder()
{
#ifdef	_DEBUG
	m_strValue.SetEmpty();
#endif
}

// Sets or gets the length of the String in this buffer.
// If length is less than the current instance, the string is truncated.
// If length is greater than the current  instance, NULLs are appended.
// The capacity is adjusted to be the same as the length.
void REFUniStringBuilder::SetLength(int nLength)
{
	BFX_REQUIRE (nLength >= 0);
	if (nLength == 0)
	{
		// the user is trying to clear the string
		m_strValue.SetEmpty();
		return;
	}

	int nOldLength = m_strValue.GetLength();
	if (nLength == nOldLength)
	{
		// Jump out early if our requested length equals current length.
		// This will be a pretty rare branch.
		return;
	}

	UCHAR* pszBuffer = m_strValue.GetBuffer(nLength);
	if (nLength > nOldLength)
	{
		// Fills NULLs to extra-spaces.
		memset(pszBuffer + nOldLength, 0, nLength - nOldLength);
	}
	pszBuffer[nLength] = 0;
	m_strValue.SetLength(nLength);
}

int REFUniStringBuilder::GetLength() const
{
	return m_strValue.GetLength();
}

UCSTR REFUniStringBuilder::GetData() const
{
	return m_strValue.GetData();
}

UniString REFUniStringBuilder::ToString() const
{
	return m_strValue;
}

// Appends a character at the end of this string builder.
void REFUniStringBuilder::Append(UCHAR ch, int nRepeatCount)
{
	BFX_REQUIRE (nRepeatCount >= 0);

	if (nRepeatCount == 0)
		return;

	int nOldLength = m_strValue.GetLength();
	int nNewLength = m_strValue.GetLength() + nRepeatCount;
	UCHAR* pszBuffer = m_strValue.GetBuffer(nNewLength);
	for (int i = 0; i < nRepeatCount; i ++)
	{
		pszBuffer[nOldLength + i] = ch;
	}
	pszBuffer[nNewLength] = 0;	// '\0'
	m_strValue.SetLength(nNewLength);
}

// Appends an array of characters at the end of this string builder.
void REFUniStringBuilder::Append(UCSTR psz, int nOffset, int nLength)
{
	m_strValue.Append(psz + nOffset, nLength);
}

// Appends a copy of this string at the end of this string builder.
void REFUniStringBuilder::Append(const UniString str)
{
	m_strValue.Append(str);
}

// Appends a new line.
void REFUniStringBuilder::AppendLine()
{
#ifdef	_WIN32
	m_strValue.Append('\r');
#endif
	m_strValue.Append('\n');
}

// Appends a copy of string with new line.
void REFUniStringBuilder::AppendLine(const UniString& str)
{
	m_strValue.Append(str);
	AppendLine();
}

// Inserts a string into this string builder.
void REFUniStringBuilder::Insert(int nOffset, const UniString& str)
{
	BFX_REQUIRE (nOffset >= 0 && nOffset <= GetLength());
	if (str.IsEmpty())
		return;

	int nInsertLength = str.GetLength();
	int nOldLength = m_strValue.GetLength();
	int nNewLength = nOldLength + nInsertLength;
	BFX_ENSURE (nNewLength > 0);	// Overflow

	UCHAR* pszBuffer = m_strValue.GetBuffer(nNewLength);

	// STEP 1: shift existing data up, to make-sure we have enough space to contains new data.
	int nCharsToShift = nOldLength - nOffset;
	if (nCharsToShift > 0)
		memmove(pszBuffer + nOffset + nInsertLength, pszBuffer + nOffset, nCharsToShift * sizeof(UCHAR));

	// STEP 2: copy new data to offset.
	memmove(pszBuffer + nOffset, str.GetData(), nInsertLength * sizeof(UCHAR));

	pszBuffer[nNewLength] = 0;	// '\0'
	m_strValue.SetLength(nNewLength);
}

// Removes the specified characters from this string builder.
void REFUniStringBuilder::Remove(int nOffset, int nLength)
{
	BFX_REQUIRE (nLength >= 0 && nOffset >= 0);
	BFX_REQUIRE (nLength >= m_strValue.GetLength() - nOffset);
	if (nLength == 0)
		return;

	int nOldLength = m_strValue.GetLength();
	int nNewLength = nOldLength - nLength;
	UCHAR* pszBuffer = m_strValue.GetBuffer(nOldLength);

	// shift existing data down.
	int nCharsToShift = nOldLength - (nOffset + nLength);
	if (nCharsToShift > 0)
		memmove(pszBuffer + nOffset, pszBuffer + nOffset + nLength, nCharsToShift * sizeof(UCHAR));

	pszBuffer[nNewLength] = 0;	// '\0'
	m_strValue.SetLength(nNewLength);
}

}	//	namespace BFX
